import { ResultEntity } from "./index";

interface ICacheData<T = any>  {
    /**
     * 保存数据的时间
     */
    time: number
    /**
     * 删除标识：用于执行增删改之后，给数据打上已经删除的标识
     */
    del?: boolean
    /**
     * 保存的数据
     */
    data: T
}

// 存储缓存数据的对象，使用Map所以是内存缓存（页面刷新后缓存数据消失）
const map = new Map<string,ICacheData>();

// 缓存数据的有效时长
const duration = 1000 * 60 * 5;

// 不缓存（或者不清空缓存）的接口url开头字段
const excludeStarts = ["/login","/code"]

// 不缓存且清空缓存的方法
const excludeMethods = ["delete","update","insert"]

/**
 * 通过key获取缓存的数据
 * @param key 缓存的key
 * @returns 缓存的数据，如果没有数据或者数据过期返回null
 */
function load(key:string,offline?:boolean): ResultEntity | null {
    const cacheData = map.get(key);
    if (null == cacheData) {
        return null;
    }
    // 如果时间为负数，那么表示永久存储
    if (duration < 0) {
        return cacheData.data;
        // 如果时间为0，表示不缓存
    } else if (duration == 0) {
        return null;
    } else {
        // 惰性计算失效数据，当读取到数据的时候，判断数据是否已经失效，失效后删除数据并返回null
        const now = new Date();
        if (now.getTime() - cacheData.time >= duration) {
            // 删除数据
            map.delete(key);
            return null;
        }
        if (offline) {
            return cacheData.data;
        } else {
            console.log("缓存命中：",key);
            if (cacheData.del) {
                return null;
            }
            return cacheData.data;
        }
        
    }
}

/**
 * 在本地缓存中存储数据，或清空缓存数据（执行增、删、改操作时）
 * @param {String} url 请求地址，请求地址遵循 /命名空间/方法名/的格式
 * @param {String} jsonData 请求参数的json字符串
 * @param {String} methodStr 请求方法 "get"、"post"等等
 * @param {Object} data 要缓存的数据
 * @returns undefined
 */
function save(url = "",
    key:string,
    data:object) {
    // 某些接口不需要缓存数据
    const flag1 = excludeStarts.some(v => url.startsWith(v));
    if (flag1) {
        return;
    }
    // 获取url地址中的方法，/admin/insert => /命名空间/方法 => 方法为insert
    const method = url.substring(url.indexOf("/",1) + 1);
    // update,delete,insert不需要缓存，且会清空同样命名空间中的所有数据
    const flag2 = excludeMethods.some(v => method.startsWith(v));
    // 清空命名空间中的缓存数据
    if (flag2) {
        // 获取url地址中的命名空间 /admin/insert => /admin/
        const namespace = url.substring(0,url.indexOf("/",1) + 1);
        clearNamespace(namespace);
        return;
    }
    const time = new Date().getTime();
    // 将数据放入到缓存中，同时记录当前时间
    map.set(key,{
        time,
        data
    });
}


/**
 * 清空所有缓存数据
 */
 function clearAll() {
    console.log("清空所有缓存！");
    Array.from(map.values()).forEach(v => {
        v.del = true;
    })
}

/**
 * 清空命名空间中的数据
 * @param namespace 命名空间，如/admin/
 */
function clearNamespace(namespace:string) {
    if (!namespace.startsWith("/")) {
        namespace = "/" + namespace;
    }
    console.log("清空命名空间缓存：",namespace);
    Array.from(map.keys()).forEach(v => {
        if (v.startsWith(namespace)) {
            const obj = map.get(v);
            if (obj) {
                obj.del = true;  
            }
        }
    })
}

/**
 *
 * 将存储的数据做本地持久化，同时也将本地化存储的数据加载出来。
 * @param type 存储方式：1为sessionStorage（默认），2为localStorage，
 */
function persist(type: 0 | 1 | 2 = 0) {
    // 在页面卸载时将map里的信息保存到本地存储里
    window.addEventListener("beforeunload", () => {
        console.log("持久化axion缓存数据！")
        const now = new Date();
        const map2:any = {};
        map.forEach((value:any,key:string) => {
            if (now.getTime() - value.time < duration) {
                map2[key] = value;
            }
        })
        if (type == 2) {
            localStorage.setItem("localcache-data", JSON.stringify(map2));
        } else {
            sessionStorage.setItem("localcache-data", JSON.stringify(map2));
        }     
    });

    let jsonStr:string | null;
    if (type == 2) {
        jsonStr = localStorage.getItem("localcache-data");
    } else {
        jsonStr = sessionStorage.getItem("localcache-data")
    }
    if (!jsonStr) {
        return;
    }
    try {
        const obj = JSON.parse(jsonStr);
        for (const key in obj) {
            map.set(key,obj[key]);
        }
        console.log("加载axios缓存数据成功")
    } catch (e) {
        console.log("从本地化数据中加载缓存出错:",e)
    }
}

export {load,save,clearAll,clearNamespace,persist}